<?php

declare(strict_types=1);

namespace app\service\org;

use app\model\OrgMember;
use app\model\Org;
use app\model\User;
use InvalidArgumentException;
use think\facade\Db;
use think\Paginator;

class OrgMemberService
{
    public function paginate(array $filters, int $page, int $pageSize): Paginator
    {
        $query = OrgMember::with(['org', 'user.profile'])
            ->order('id', 'desc');

        if (!empty($filters['org_id'])) {
            $query->where('org_id', (int) $filters['org_id']);
        }

        if (!empty($filters['user_id'])) {
            $query->where('user_id', (int) $filters['user_id']);
        }

        if (!empty($filters['member_role'])) {
            $query->where('member_role', trim((string) $filters['member_role']));
        }

        if (isset($filters['status']) && $filters['status'] !== '') {
            $query->where('status', (int) $filters['status']);
        }

        if (!empty($filters['org_type'])) {
            $orgType = trim((string) $filters['org_type']);
            $query->hasWhere('org', static function ($subQuery) use ($orgType) {
                $subQuery->where('org_type', $orgType);
            });
        }

        return $query->paginate([
            'list_rows' => $pageSize,
            'page' => $page,
        ]);
    }

    public function findById(int $id): ?OrgMember
    {
        return OrgMember::with(['org', 'user.profile'])->find($id);
    }

    public function create(array $payload): OrgMember
    {
        $data = $this->extractPayload($payload, true);
        $this->ensureRelations($data['org_id'], $data['user_id']);
        $this->assertUniqueMember($data['org_id'], $data['user_id']);

        /** @var OrgMember $member */
        $member = Db::transaction(function () use ($data) {
            $data['joined_at'] = $data['joined_at'] ?? date('Y-m-d H:i:s');
            /** @var OrgMember $created */
            $created = OrgMember::create($data);
            $this->refreshOrgOwner((int) $created->org_id, (int) $created->user_id, $created->member_role);
            return $created;
        });

        return $this->findById((int) $member->id);
    }

    public function update(OrgMember $member, array $payload): OrgMember
    {
        $data = $this->extractPayload($payload, false);

        if (array_key_exists('org_id', $data) || array_key_exists('user_id', $data)) {
            $orgId = $data['org_id'] ?? (int) $member->org_id;
            $userId = $data['user_id'] ?? (int) $member->user_id;
            $this->ensureRelations($orgId, $userId);

            if ($orgId !== (int) $member->org_id || $userId !== (int) $member->user_id) {
                $this->assertUniqueMember($orgId, $userId, (int) $member->id);
            }
        }

        Db::transaction(function () use ($member, $data) {
            $member->save($data);
            $member->refresh();
            $this->refreshOrgOwner((int) $member->org_id, (int) $member->user_id, $member->member_role);
        });

        return $this->findById((int) $member->id);
    }

    public function delete(OrgMember $member): bool
    {
        return (bool) $member->delete();
    }

    public function format(OrgMember $member): array
    {
        return [
            'id' => (int) $member->id,
            'org_id' => (int) $member->org_id,
            'user_id' => (int) $member->user_id,
            'member_role' => $member->member_role,
            'position' => $member->position,
            'status' => (int) $member->status,
            'invited_by' => $member->invited_by ? (int) $member->invited_by : null,
            'joined_at' => $member->joined_at,
            'create_time' => $member->create_time,
            'update_time' => $member->update_time,
            'org' => $member->org ? [
                'id' => (int) $member->org->id,
                'name' => $member->org->name,
                'org_type' => $member->org->org_type,
            ] : null,
            'user' => $member->user ? array_merge(
                $member->user->toArray(),
                [
                    'profile' => $member->user->profile ? $member->user->profile->toArray() : null,
                ]
            ) : null,
        ];
    }

    private function extractPayload(array $payload, bool $isCreate): array
    {
        $data = [];

        if ($isCreate || array_key_exists('org_id', $payload)) {
            $orgId = (int) ($payload['org_id'] ?? 0);
            if ($orgId <= 0) {
                throw new InvalidArgumentException('组织ID不能为空');
            }
            $data['org_id'] = $orgId;
        }

        if ($isCreate || array_key_exists('user_id', $payload)) {
            $userId = (int) ($payload['user_id'] ?? 0);
            if ($userId <= 0) {
                throw new InvalidArgumentException('用户ID不能为空');
            }
            $data['user_id'] = $userId;
        }

        if ($isCreate || array_key_exists('member_role', $payload)) {
            $role = trim((string) ($payload['member_role'] ?? ''));
            if (!in_array($role, [OrgMember::ROLE_OWNER, OrgMember::ROLE_ADMIN, OrgMember::ROLE_MEMBER], true)) {
                throw new InvalidArgumentException('成员角色不正确');
            }
            $data['member_role'] = $role;
        }

        if ($isCreate || array_key_exists('position', $payload)) {
            $data['position'] = (string) ($payload['position'] ?? '');
        }

        if ($isCreate || array_key_exists('status', $payload)) {
            $status = (int) ($payload['status'] ?? 1);
            if (!in_array($status, [0, 1], true)) {
                throw new InvalidArgumentException('成员状态不正确');
            }
            $data['status'] = $status;
        }

        if ($isCreate || array_key_exists('invited_by', $payload)) {
            $invited = $payload['invited_by'] ?? null;
            $data['invited_by'] = $invited !== null && $invited !== '' ? (int) $invited : null;
        }

        if ($isCreate || array_key_exists('joined_at', $payload)) {
            $joinedAt = $payload['joined_at'] ?? null;
            if ($joinedAt !== null && $joinedAt !== '') {
                $data['joined_at'] = (string) $joinedAt;
            }
        }

        return $data;
    }

    private function ensureRelations(int $orgId, int $userId): void
    {
        if (Org::where('id', $orgId)->count() === 0) {
            throw new InvalidArgumentException('组织不存在');
        }

        if (User::where('id', $userId)->count() === 0) {
            throw new InvalidArgumentException('用户不存在');
        }
    }

    private function assertUniqueMember(int $orgId, int $userId, ?int $excludeId = null): void
    {
        $query = OrgMember::where('org_id', $orgId)
            ->where('user_id', $userId);

        if ($excludeId !== null) {
            $query->where('id', '<>', $excludeId);
        }

        if ($query->count() > 0) {
            throw new InvalidArgumentException('该用户已是组织成员');
        }
    }

    private function refreshOrgOwner(int $orgId, int $userId, string $role): void
    {
        if ($role !== OrgMember::ROLE_OWNER) {
            return;
        }

        Org::where('id', $orgId)->update(['owner_user_id' => $userId]);
    }
}
